package com.huxiaoshou.exam.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huxiaoshou.exam.common.CacheConstants;
import com.huxiaoshou.exam.entity.Question;
import com.huxiaoshou.exam.entity.QuestionAnswer;
import com.huxiaoshou.exam.entity.QuestionChoice;
import com.huxiaoshou.exam.mapper.QuestionAnswerMapper;
import com.huxiaoshou.exam.mapper.QuestionChoiceMapper;
import com.huxiaoshou.exam.mapper.QuestionMapper;
import com.huxiaoshou.exam.service.QuestionService;
import com.huxiaoshou.exam.utils.RedisUtils;
import com.huxiaoshou.exam.vo.QuestionQueryVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 题目Service实现类
 * 实现题目相关的业务逻辑
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private QuestionAnswerMapper questionAnswerMapper;

    @Autowired
    private QuestionChoiceMapper questionChoiceMapper;

    @Override
    public void customPageService(Page<Question> pageBean, QuestionQueryVo questionQueryVo) {
        questionMapper.customPage(pageBean, questionQueryVo);
    }

    @Override
    public Question customDetailQuestion(Long id) {
        Question question = questionMapper.customGetById(id);
        if (question == null) {
            throw new RuntimeException("题目查询详情失败！原因可能提前被删除！题目id为：" + id);
        }

        // 缓存热门题目
        new Thread(() -> incrementQuestion(question.getId())).start();

        return question;
    }

    private void incrementQuestion(Long questionId) {
        Double score = redisUtils.zIncrementScore(CacheConstants.POPULAR_QUESTIONS_KEY, questionId, 1);
        log.info("完成{}题目分数累计，累计后分数为：{}", questionId, score);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void customSaveQuestion(Question question) {
        boolean exists = this.lambdaQuery()
                .eq(Question::getType, question.getType())
                .eq(Question::getTitle, question.getTitle())
                .exists();

        if (exists) {
            throw new RuntimeException("在%s下，存在%s 名称的题目已经存在！保存失败！".formatted(question.getType(), question.getTitle()));
        }

        boolean saved = this.save(question);
        if (!saved) {
            throw new RuntimeException("在%s下，存在%s 名称的题目！保存失败！".formatted(question.getType(), question.getTitle()));
        }

        QuestionAnswer answer = question.getAnswer();
        answer.setQuestionId(question.getId());

        // 判断是不是选择题
        if ("CHOICE".equals(question.getType())) {
            List<QuestionChoice> choices = question.getChoices();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < choices.size(); i++) {
                QuestionChoice choice = choices.get(i);
                choice.setSort(i);
                choice.setQuestionId(question.getId());
                questionChoiceMapper.insert(choice);
                if (choice.getIsCorrect()) {
                    if (!sb.isEmpty()) {
                        sb.append(",");
                    }
                    sb.append((char) ('A' + i));
                }
            }

            answer.setAnswer(sb.toString());
        }

        questionAnswerMapper.insert(answer);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void customUpdateQuestion(Question question) {
        boolean exists = this.lambdaQuery()
                .eq(Question::getTitle, question.getTitle())
                .eq(Question::getId, question.getId())
                .exists();
        if (exists) {
            throw new RuntimeException("修改：%s题目的新标题：%s和其他的题目重复了！修改失败！".formatted(question.getId(), question.getTitle()));
        }

        boolean updated = this.updateById(question);
        if (!updated) {
            throw new RuntimeException("修改：%s题目失败！！".formatted(question.getId()));
        }

        QuestionAnswer answer = question.getAnswer();
        if ("CHOICE".equals(question.getType())) {
            List<QuestionChoice> choiceList = question.getChoices();
            LambdaQueryWrapper<QuestionChoice> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(QuestionChoice::getQuestionId, question.getId());
            questionChoiceMapper.delete(lambdaQueryWrapper);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < choiceList.size(); i++) {
                QuestionChoice choice = choiceList.get(i);
                choice.setId(null);
                choice.setSort(i);
                choice.setCreateTime(null);
                choice.setUpdateTime(null);
                choice.setQuestionId(question.getId());
                questionChoiceMapper.insert(choice);
                if (choice.getIsCorrect()) {
                    if (!sb.isEmpty()) {
                        sb.append(",");
                    }
                    sb.append((char) ('A' + i));
                }
            }

            answer.setAnswer(sb.toString());
        }

        questionAnswerMapper.updateById(answer);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void customRemoveQuestionById(Long id) {
        Long count = this.lambdaQuery()
                .eq(Question::getId, id)
                .count();
        if (count > 0) {
            throw new RuntimeException("该题目：%s 被试卷表中引用%s次，删除失败！".formatted(id, count));
        }

        // 删除题目表
        boolean removed = this.removeById(id);
        if (!removed) {
            throw new RuntimeException("该题目：%s 信息删除失败！！");
        }

        // 删除答案表
        questionAnswerMapper.delete(
                new LambdaQueryWrapper<QuestionAnswer>()
                        .eq(QuestionAnswer::getQuestionId, id)
        );

        // 删除选项表
        questionChoiceMapper.delete(
                new LambdaQueryWrapper<QuestionChoice>()
                        .eq(QuestionChoice::getQuestionId, id)
        );
    }

    @Override
    public List<Question> customFindPopularQuestions(Integer size) {
        List<Question> popularQuestions = new ArrayList<>();

        Set<ZSetOperations.TypedTuple<Object>> tupleSet = redisUtils.zReverseRangeWithScores(CacheConstants.POPULAR_QUESTIONS_KEY, 0, size - 1);

        List<Long> idsSet = new ArrayList<>();
        if (tupleSet != null && !tupleSet.isEmpty()) {
            List<Long> idsList = tupleSet.stream()
                    .sorted((o1, o2) -> Integer.compare(Objects.requireNonNull(o2.getScore()).intValue(), Objects.requireNonNull(o1.getScore()).intValue()))
                    .map(o -> Long.valueOf(Objects.requireNonNull(o.getValue()).toString()))
                    .collect(Collectors.toList());

            idsSet.addAll(idsList);
            log.debug("从redis获取热门题目的id集合，且保证顺序：{}", idsList);

            for (Long id : idsList) {
                Question question = getById(id);
                if (question != null) {
                    popularQuestions.add(question);
                }
            }

            log.debug("去redis查询的热门题目，题目数：{},题目内容为：{}", popularQuestions.size(), popularQuestions);
        }

        int diff = size - popularQuestions.size();
        if (diff > 0) {
            List<Question> questionDiffList = this.lambdaQuery()
                    .notIn(Question::getId, idsSet)
                    .orderByDesc(Question::getCreateTime)
                    .last("limit " + diff)
                    .list();

            log.debug("去question表中补充热门题目，题目数：{},题目内容为：{}", questionDiffList.size(), questionDiffList);
            if (!questionDiffList.isEmpty()) {
                popularQuestions.addAll(questionDiffList);
            }
        }

//        fillQuestionChoiceAndAnswer(popularQuestions);
        return popularQuestions;
    }

}